﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace WooCoo.DataPortal.Client
{
    /// <summary>
    /// 执行表达式计算类,对可求得结果表达式进行结果赋值
    /// </summary>
    public static class Evaluator
    {
        /// <summary>
        /// 执行赋值计算并替换独立子树
        /// </summary>
        /// <param name="expression">根表达式.</param>
        /// <param name="canBeEvaluated">Func委托用于判断表达式是否可在本地进行计算得值</param>
        /// <returns>子树进行赋值计算并替换的新表达式树.</returns>
        public static Expression PartialEval(Expression expression, Func<Expression, bool> canBeEvaluated)
        {
            Nominator nominator = new Nominator(canBeEvaluated);
            HashSet<Expression> candidates = nominator.Nominate(expression);
            return new SubtreeEvaluator(candidates).Eval(expression);
        }

        /// <summary>
        /// 执行赋值计算并替换独立子树
        /// </summary>
        /// <param name="expression">根表达式.</param>
        /// <returns>子树进行赋值计算并替换的新表达式树.</returns>
        public static Expression PartialEval(Expression expression)
        {
            return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
        }

        /// <summary>
        /// 判断表达式是否可在本地进行计算得值
        /// </summary>
        /// <param name="expression">表达式.</param>
        /// <returns>true/ false 如果可在本地计算得值</returns>
        private static bool CanBeEvaluatedLocally(Expression expression)
        {
            return expression.NodeType != ExpressionType.Parameter &&
                expression.NodeType != ExpressionType.Lambda &&
                expression.NodeType != (ExpressionType)ResourceExpressionType.RootResourceSet;
        }

        /// <summary>
        /// 子树计算处理类
        /// </summary>
        internal class SubtreeEvaluator : DataPortalALinqExpressionVisitor
        {
            /// <summary>list of candidates</summary>
            private HashSet<Expression> candidates;

            /// <summary>
            /// 通过需要计算的子树列表构造方法
            /// </summary>
            /// <param name="candidates">需要计算的子树列表</param>
            internal SubtreeEvaluator(HashSet<Expression> candidates)
            {
                this.candidates = candidates;
            }

            /// <summary>
            /// 子表达式求值计算
            /// </summary>
            /// <param name="exp">需要计算的表达式.</param>
            /// <returns>计算完成的表达式.</returns>
            internal Expression Eval(Expression exp)
            {
                return this.Visit(exp);
            }

            /// <summary>
            /// 遍历方法
            /// </summary>
            /// <param name="exp">the expression to visit</param>
            /// <returns>visited expression</returns>
            public override Expression Visit(Expression exp)
            {
                if (exp == null)
                {
                    return null;
                }

                if (this.candidates.Contains(exp))
                {
                    return Evaluate(exp);
                }

                return base.Visit(exp);
            }

            /// <summary>
            /// 表达式求值
            /// </summary>
            /// <param name="e">需要计算值的表达式</param>
            /// <returns>完成赋值计算的常量表达式</returns>
            private static Expression Evaluate(Expression e)
            {
                if (e.NodeType == ExpressionType.Constant)
                {
                    return e;
                }

#if ASTORIA_LIGHT
                LambdaExpression lambda = ExpressionHelpers.CreateLambda(e, new ParameterExpression[0]); 
#else
                LambdaExpression lambda = Expression.Lambda(e);
#endif
                Delegate fn = lambda.Compile();
                object constantValue = fn.DynamicInvoke(null);
                Debug.Assert(!(constantValue is Expression), "!(constantValue is Expression)");

                // Use the expression type unless it's an array type,
                // where the actual type may be a vector array rather
                // than an array with a dynamic lower bound.
                Type constantType = e.Type;
                if (constantValue != null && constantType.IsArray && constantType.GetElementType() == constantValue.GetType().GetElementType())
                {
                    constantType = constantValue.GetType();
                }

                return Expression.Constant(constantValue, constantType);
            }
        }

        /// <summary>
        /// 执行自下而上的分析以确定哪些节点可能是计算后子树的一部分
        /// </summary>
        internal class Nominator : DataPortalALinqExpressionVisitor
        {
            /// <summary>Func委托用于判断表达式是否可在本地进行计算得值</summary>
            private Func<Expression, bool> functionCanBeEvaluated;

            /// <summary>需要申请计算的表达式集合</summary>
            private HashSet<Expression> candidates;

            /// <summary>子表达式不可被计算标识</summary>
            private bool cannotBeEvaluated;

            /// <summary>
            /// 通过传入的委托创建Nominator.
            /// </summary>
            /// <param name="functionCanBeEvaluated">
            /// 用于判断表达式是否可进行计算得值的Func
            /// </param>
            /// <returns>被遍历的表达式</returns>
            internal Nominator(Func<Expression, bool> functionCanBeEvaluated)
            {
                this.functionCanBeEvaluated = functionCanBeEvaluated;
            }

            /// <summary>
            /// 获取可被计算的子树集合
            /// </summary>
            /// <param name="expression">
            /// 需要检查的表达式
            /// </param>
            /// <returns>可被计算的子树集合</returns>
            internal HashSet<Expression> Nominate(Expression expression)
            {
                this.candidates = new HashSet<Expression>(EqualityComparer<Expression>.Default);
                this.Visit(expression);
                return this.candidates;
            }

            /// <summary>
            /// 自下而上的遍历表达式方法
            /// </summary>
            /// <param name="expression">
            /// 被遍历的表达式
            /// </param>
            /// <returns>完成遍历的表达式</returns>
            public override Expression Visit(Expression expression)
            {
                if (expression != null)
                {
                    bool saveCannotBeEvaluated = this.cannotBeEvaluated;
                    this.cannotBeEvaluated = false;

                    base.Visit(expression);

                    if (!this.cannotBeEvaluated)
                    {
                        if (this.functionCanBeEvaluated(expression))
                        {
                            this.candidates.Add(expression);
                        }
                        else
                        {
                            this.cannotBeEvaluated = true;
                        }
                    }

                    this.cannotBeEvaluated |= saveCannotBeEvaluated;
                }

                return expression;
            }
        }
    } 
}
